/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.JobName;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;

import org.apache.log4j.Logger;
import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.bushe.swing.event.annotation.EventSubscriber;

import who.tmap.bo.BaseMap;
import who.tmap.bo.BizObjects;
import who.tmap.bo.MultiLayerThematicMap;
import who.tmap.bo.ThematicMap;
import who.tmap.bo.ThematicMapLayer;
import who.tmap.bo.impl.BizObjectFactory;
import who.tmap.common.TMapException;
import who.tmap.toolkit.MultiLayerThematicMapToolKit;
import who.tmap.toolkit.impl.MappingToolKitFactory;
import who.tmap.toolkit.shape.MapShape;
import who.tmap.ui.event.DataStructureHasChanged;
import who.tmap.ui.event.MultiLayerCheckBoxListClicked;
import who.tmap.ui.event.ThematicMapLayerUpdated;
import who.tmap.ui.util.ArgumentsHelper;
import who.tmap.ui.util.CheckBoxList;
import who.tmap.ui.util.ExcelMacroHelper;
import who.tmap.ui.util.FileChooserFilter;
import who.tmap.ui.util.MiscUIHelper;
import who.tmap.ui.util.MultiLayerThematicMapPopupMenu;
import who.tmap.ui.util.MultiLayerThematicMapUIHelper;
import who.tmap.ui.util.RecentFilesHelper;
import who.tmap.ui.util.ThematicMapCheckBoxListRenderer;
import who.tmap.ui.util.ThematicMapUIHelper;
import who.tmap.util.MiscHelper;

/**
 * 
 * 
 * @author stl
 *
 */
public class JFrameMultiLayerThematicMap extends JFrame {

    private static Logger logger = Logger.getLogger(JFrameMultiLayerThematicMap.class);  
    
    private static final String TITLE ="TMap Designer";
	
	private MiscHelper miscHelper =new MiscHelper();
	private MiscUIHelper miscUIHelper =new MiscUIHelper();
	private MultiLayerThematicMapUIHelper multiLayerUIHelper =new MultiLayerThematicMapUIHelper();
	private MultiLayerThematicMapToolKit mltk =MappingToolKitFactory.getInstance().getMultiLayerGraphicToolKit();
	private MultiLayerThematicMap mltm =null;	
	
	private CheckBoxList jList;
	private JPanelMap rightSide;
	private MultiLayerThematicMapPopupMenu popupMenu;
	private JMenu menuItemFileRecentFiles;
	private JSeparator separatorFileRecentFiles =new JSeparator();
	
    private RecentFilesHelper recFileHelper = RecentFilesHelper.getInstance();
	
	private JFrameMultiLayerThematicMap thisFrame =this;
	
	private boolean dirty =false;
	
	public JFrameMultiLayerThematicMap() {
		super();
		
		initialize();
	}
	
	private void initialize() {
		
		setSize( 700, 650 );
		
		setJMenuBar( getTheMenuBar() );
		
		getContentPane().add( getJToolBar(), BorderLayout.NORTH );
		getContentPane().add( getJSplitPane(), BorderLayout.CENTER );	
		
		setLocationRelativeTo( null );
		setTitle( TITLE );
		
		setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
		
		addWindowListener( new WindowAdapter() {   
			public void windowClosing(WindowEvent e) {
				quitApplication();
			}
		});
		
		setIconImage( miscHelper.getImageIcon( "ic_tmapdesigner.gif" ).getImage() );
		
		mltk.setContainer( getRightSide() );
		try {
			mltm =((MultiLayerThematicMap)multiLayerUIHelper.newMultiLayerThematicMap());
			mltm.setLayers( new ArrayList<ThematicMapLayer>() );
		} catch (TMapException e1) {
			logger.error( e1 );
		}
		mltk.setMultiLayerThematicMap( mltm );
		
		multiLayerUIHelper.setMultiLayerToolKit( mltk );
		
		AnnotationProcessor.process(this);
		
		processArguments();
	}	
	
	private void processArguments() {
		ArgumentsHelper argumentsHelper =ArgumentsHelper.getInstance();
		
		String name =argumentsHelper.getValue( ArgumentsHelper.NAME );
		if ( name != null ) {
			logger.debug( ArgumentsHelper.NAME + "=" + name );
			
			if ( !new File( name ).exists() ) {
				
				// We don't use log4j here as it is an output for command-line users
				System.out.println( "Cannot find file " + name );
				dispose();
				
			} else if ( name.toLowerCase().endsWith( FileChooserFilter.EXCEL_MACRO_EXT ) ) {
				
				Integer errCode =0;
				String errMsg ="";
				
				ExcelMacroHelper emh =new ExcelMacroHelper();
				
				try {
					
					emh.generateThematicMapFromExcelMacro( name );
						
					ThematicMapLayer layer =multiLayerUIHelper.loadLayer( new File( emh.getTmapSourceFileName() ) );
					
					//layer.getThematicMap().setData( emh.getAttrs() );
					
					emh.generateThematicMap( layer.getThematicMap() );
					
					if ( layer != null && !layerNameAlreadyExists( layer.getName() ) ) {
						getJList().add( layer );
						
						getRightSide().repaint();
					}
					
					updateMLTM();
					
					setVisible( true );
					
					getRightSide().repaint();
					
					boolean success =mltk.saveThematicMapAsBitmap( new File( emh.getImageTargetFileName() ),
																   // Remove leading dot
																   FileChooserFilter.PNG_EXT.substring(1),
																   getRightSide().getWidth(), 
																   getRightSide().getHeight() );
					
				} catch (Exception e) {
					logger.error( e );
					errMsg =e.toString();
					errCode =-1;
				}
				
				// Generate return code file even when the map generation has failed
				emh.generateReturnCodeFile( errCode, errMsg );
				
				if ( argumentsHelper.contains( ArgumentsHelper.QUIET ) ) {
					dispose();
				}
				
			} else if ( name.toLowerCase().endsWith( FileChooserFilter.MULTI_LAYER_THEMATIC_MAP_EXT ) ) {
				//setVisible( true );
				//jFrameMultiLayerThematicMap =new JFrameMultiLayerThematicMap();
				//jFrameMultiLayerThematicMap.setVisible( true );
				
				//new MultiLayerThematicMapUIHelper().
				multiLayerUIHelper.openMultiLayerThematicMapFromArgument( name, this );
				
				if ( argumentsHelper.contains( ArgumentsHelper.PRINT ) ) {
					printThematicMap();
				} else if ( name != null ) {
					displayFullScreen();
				}
				
				if ( argumentsHelper.contains( ArgumentsHelper.QUIET ) ) {
					dispose();
				} else {
					setVisible( true );
				}
				
			} else {
				//new ThematicMapUIHelper( jFrameThematicMap ).openThematicMapFromArgument( name );
				//multiLayerUIHelper.openMultiLayerThematicMapFromArgument( name, this );
				
				ThematicMapLayer layer =null;
				try {
					layer = (ThematicMapLayer)multiLayerUIHelper.loadLayer( new File( name ) );
					
					if ( layer != null && !layerNameAlreadyExists( layer.getName() ) ) {
						getJList().add( layer );
						
						getRightSide().repaint();
					}
				
					if ( argumentsHelper.contains( ArgumentsHelper.RELOAD ) ) {
						//jFrameThematicMap.reloadFromDataSource( false );
					}
					
					if ( argumentsHelper.contains( ArgumentsHelper.PRINT ) ) {
						printThematicMap();
					} else if ( name != null ){
						displayFullScreen();
					}
				
				} catch (TMapException e) {
					e.printStackTrace();
				}
				
				if ( argumentsHelper.contains( ArgumentsHelper.QUIET ) ) {
					dispose();
				}		
			}
		}
		
	}	
	
	private void quitApplication() {
		if ( saveRequired() != JOptionPane.CANCEL_OPTION ) {
			miscUIHelper.closeHelp();
			/*closeHelp();
			try {
				new ConfigFileHelper().save( jFrameMain );
			} catch (IOException e1) {
				logger.error( e1 );
			}	*/	
			try {
				recFileHelper.saveFiles();
			} catch(IOException e) {
				logger.error(e);
			}		
			
			logger.info( "TMap Designer is closing." );
			System.exit(0);
		} 
	}
	
	private void displayTitle() {
		String title =TITLE;
		
		if ( mltm.getFile() != null && !"".equals( mltm.getFile() ) ) {
			title+=" - " + mltm.getFile();
		}
		
		setTitle( title );
	}
	
	private JSplitPane getJSplitPane() {
        JSplitPane splitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, getLeftSide(), getRightSide() );
        
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(190);
		splitPane.setContinuousLayout( false );
		
		return splitPane;
	}
	
	private Component getLeftSide() {
		JPanel lsPanel =new JPanel();	
		lsPanel.setLayout( new BorderLayout() );
		lsPanel.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
		
		lsPanel.add( new JLabel( "Layers" ), BorderLayout.NORTH );
		lsPanel.add( getJList(), BorderLayout.CENTER );
		lsPanel.add( getLeftSideButtons(), BorderLayout.SOUTH );
		
		return lsPanel;
	}
	
	private JPanel getLeftSideButtons() {
		JPanel panel =new JPanel();
		
		JPanel lsbPanel =new JPanel();
		panel.add( lsbPanel, null );
		lsbPanel.setLayout( new GridLayout( 4, 2, 5, 5 ) );
		
		lsbPanel.add( getJButtonNew(), null );
		lsbPanel.add( getJButtonEdit(), null );
		lsbPanel.add( getJButtonAdd(), null );
		lsbPanel.add( getJButtonDel(), null );
		lsbPanel.add( getJButtonUp(), null );		
		lsbPanel.add( getJButtonDown(), null );
		lsbPanel.add( getJButtonRename(), null );
		lsbPanel.add( getJButtonDuplicate(), null );		
		
		return panel;
	}
	
	private boolean layerNameAlreadyExists( String layerName ) {
		for( int ct =0; ct < getJList().elements().length; ct++ ) {
			
			ThematicMapLayer layer =(ThematicMapLayer)getJList().elements()[ ct ];
			
			if ( layerName.equals( layer.getName() ) ) {
				
				JOptionPane.showMessageDialog( null, "A layer with the same name already exists."
												   , "Thematic map layer", JOptionPane.WARNING_MESSAGE ); 
				
				return true;
			}
		}
		
		return false;
	}

	private JButton getJButtonNew() {
		JButton neww =new JButton( "New" );
		
		neww.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				
				String layerName =JOptionPane.showInputDialog( null, "Please enter a name for this new layer"
																   , "New thematic map layer"
																   , JOptionPane.QUESTION_MESSAGE );
				
				if ( layerName != null && !"".equals( layerName.trim() ) && !layerNameAlreadyExists( layerName ) ) {

					MultiLayerThematicMapUIHelper multiLayerThematicMapUIHelper =new MultiLayerThematicMapUIHelper();
					ThematicMapLayer layer =(ThematicMapLayer)multiLayerThematicMapUIHelper.newThematicMapLayer();
					
					layer.setName( layerName );
					layer.setFile( null );//new File( layerName ) );
					
					JFrameMain jFrameMain =new JFrameMain();
					jFrameMain.setVisible( true );

					ThematicMapUIHelper thematicMapUIHelper =new ThematicMapUIHelper( jFrameMain.jFrameThematicMap );
					
					thematicMapUIHelper.applyThematicMap( jFrameMain.jFrameThematicMap );
					
					jFrameMain.setThematicMapLayer( layer );
					
					layer.setThematicMap( jFrameMain.jFrameThematicMap.getThematicMap() );

					jFrameMain.setThematicMapLayer( layer );
					
					getJList().add( layer );
					dirty =true;
					getRightSide().repaint();
					
				} else if ( layerName != null ){
					miscUIHelper.displayWarningMessage( thisFrame, "Please enter a valid name.");
					
				}
			}
		});	
		
		return neww;
	}		
	
	private JButton getJButtonEdit() {
		JButton edit =new JButton( "Edit" );
		
		edit.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {    
				int idx =getJList().getSelectedIndex();
				
				if ( idx != -1 ) {
					ThematicMapLayer layer =(ThematicMapLayer)getJList().elements()[ idx ];
					
					ThematicMap thematicMap =layer.getThematicMap();
					
					JFrameMain jFrameMain =new JFrameMain();
					
					jFrameMain.jFrameThematicMap.setThematicMap(thematicMap);
					ThematicMapUIHelper thematicMapUIHelper =new ThematicMapUIHelper( jFrameMain.jFrameThematicMap );
					
					thematicMapUIHelper.applyThematicMap( jFrameMain.jFrameThematicMap );
					
					jFrameMain.setThematicMapLayer( layer );

					jFrameMain.setVisible( true );
					
				} else {
					miscUIHelper.displayWarningMessage( thisFrame, "Please first select a layer to edit." ); 
				}
			}
		});	
		
		return edit;
	}	
	
	/*
	 * The layer has been updated by JFrameMain
	 */
    @EventSubscriber(eventClass=ThematicMapLayerUpdated.class)
    public void onEvent(ThematicMapLayerUpdated event) {
		getRightSide().repaint();
		
		dirty =true;
    }
	
	private JButton getJButtonAdd() {
		JButton add =new JButton( "Add" );
		
		add.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {    

				ThematicMapLayer layer =(ThematicMapLayer)multiLayerUIHelper.openThematicMap( null );
				
				if ( layer != null && !layerNameAlreadyExists( layer.getName() ) ) {
					getJList().add( layer );
					
					getRightSide().repaint();
					
					dirty =true;
				}
			}
		});	
		
		return add;
	}
	
	private JButton getJButtonDel() {
		JButton del =new JButton( "Delete" );
		
		del.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {    
				int idx =getJList().getSelectedIndex();
				
				if ( idx != -1 ) {
					
					if (JOptionPane.showConfirmDialog( thisFrame,
				            "Please confirm the deletion of this layer", "Delete a layer",
				            JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ) 
				            == JOptionPane.YES_OPTION ) {
					
						getJList().remove(idx);
						getRightSide().repaint();
					
						dirty =true;
					}
				} else {
					
					miscUIHelper.displayWarningMessage( thisFrame, "Please first select a layer to delete." ); 
				}
			}
		});	
		
		return del;
	}	
	
	private JButton getJButtonUp() {
		JButton up =new JButton( "Up" );
		
		up.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {    

				int srcIdx =getJList().getSelectedIndex();
				if ( srcIdx > 0 ) {
					getJList().move( srcIdx , srcIdx - 1 );
					getRightSide().repaint();
					
					dirty =true;
					
				} else if ( srcIdx == 0 ) {
					miscUIHelper.displayWarningMessage( thisFrame, "This layer cannot be move up." );
					
				} else {
					miscUIHelper.displayWarningMessage( thisFrame, "Please first select a layer to move up." );
					
				}
				
			}
		});	
		
		return up;
	}		
	
	private JButton getJButtonDown() {
		JButton down =new JButton( "Down" );
		
		down.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {    

				int srcIdx =getJList().getSelectedIndex();
				if ( srcIdx == -1 ) {
					miscUIHelper.displayWarningMessage( thisFrame, "Please first select a layer to move down." );
					
				} else if ( srcIdx == getJList().getNbElements() - 1 ) {
					miscUIHelper.displayWarningMessage( thisFrame, "This layer cannot be move down." );
					
				} else if ( srcIdx < getJList().getNbElements() - 1 ) {
					getJList().move( srcIdx , srcIdx + 1 );
					getRightSide().repaint();
					
					dirty =true;
					
				} 
				
			}
		});	
		
		return down;
	}
	
	private JButton getJButtonRename() {
		JButton rename =new JButton( "Rename" );
		
		rename.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {    

				int srcIdx =getJList().getSelectedIndex();
				if ( srcIdx == -1 ) {
					miscUIHelper.displayWarningMessage( thisFrame, "Please first select a layer to rename." );
					
				} else {
					ThematicMapLayer layer =(ThematicMapLayer)getJList().elements()[ srcIdx ];
					
					String layerName =(String) JOptionPane.showInputDialog( null, "Please enter a new name for the layer"
							   , "Rename thematic map layer"
							   , JOptionPane.QUESTION_MESSAGE, null, null, layer.getFile().getName() );

					if ( layerName != null && !"".equals( layerName.trim() ) && !layerNameAlreadyExists( layerName ) ) {
						//layer.setFile( new File( layerName ) );
						layer.setName( layerName );
												
						getJList().repaint();
						
						dirty =true;
					}
					
				} 
				
			}
		});	
		
		return rename;
	}
	
	private JButton getJButtonDuplicate() {
		JButton duplicate =new JButton( "Duplicate" );
		
		duplicate.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {    

				int srcIdx =getJList().getSelectedIndex();
				if ( srcIdx == -1 ) {
					miscUIHelper.displayWarningMessage( thisFrame, "Please first select a layer to duplicate." );
					
				} else {
					ThematicMapLayer layer =(ThematicMapLayer)getJList().elements()[ srcIdx ];
					
					String duplicatedLayerName =(String) JOptionPane.showInputDialog( null, "Please enter a name for the duplicated layer"
							   , "Rename thematic map layer"
							   , JOptionPane.QUESTION_MESSAGE, null, null, layer.getFile().getName() );

					if ( duplicatedLayerName != null && !"".equals( duplicatedLayerName.trim() ) 
							&& !layerNameAlreadyExists( duplicatedLayerName ) ) {

						try {
							ThematicMapLayer dupLayer =(ThematicMapLayer)multiLayerUIHelper.duplicateLayer( layer, duplicatedLayerName );
							
							getJList().add( dupLayer );
							
							getRightSide().repaint();
							
							dirty =true;
						} catch (TMapException e1) {
							miscUIHelper.displayErrorMessage(null, e1);
							logger.error( e1 );
						}
	
					}
					
				} 
				
			}
		});	
		
		return duplicate;
	}		
	
	private CheckBoxList getJList() {
		if ( jList == null ) {
			jList =new CheckBoxList(); 
			jList.getDataList().setCellRenderer( new ThematicMapCheckBoxListRenderer() );
			
			jList.getDataList().addMouseListener( new MouseAdapter() {
				
				private void showPopup( MouseEvent me ) {
					if ( me.isPopupTrigger() ) {
						int index = jList.getDataList().locationToIndex( me.getPoint() );
						jList.getDataList().setSelectedIndex( index );
						
						ThematicMapLayer layer =(ThematicMapLayer)jList.elements()[ index ];
						
						getPopupMenu().setLayer( layer ); 
						
						getPopupMenu().show( me.getComponent(), me.getX(), me.getY() );
					}
				}
				
				public void mousePressed( MouseEvent me ) {
					showPopup( me );
				}				
				
				public void mouseReleased( MouseEvent me ) {
					showPopup( me );
				}
			});

		}
		
		return jList;
	}
	
	private MultiLayerThematicMapPopupMenu getPopupMenu() {
		if ( popupMenu == null ) {
			popupMenu =new MultiLayerThematicMapPopupMenu();
		}
		
		return popupMenu;
	}
	
	private JPanel getRightSide() {
		if ( rightSide == null ) {
					
			rightSide =new JPanelMap();
			
			rightSide.addComponentListener( new ComponentListener() {

				public void componentHidden(ComponentEvent e) {	}

				public void componentMoved(ComponentEvent e) { }

				public void componentResized(ComponentEvent e) {
					getRightSide().repaint();
				}

				public void componentShown(ComponentEvent e) { 
					getRightSide().repaint();					
				}
				
			});
			
			rightSide.setBackground( Color.WHITE );
		}
		
		return rightSide;
	}
	
	private class JPanelMap extends JPanel {
		
		protected void paintComponent( Graphics g ) {
	        super.paintComponent( g );
	        
	        Graphics myg =g;

	        try {
	        	updateMLTM();
				mltk.repaintThematicMap( (Graphics2D)g, null );
			} catch (Exception e) {
				miscUIHelper.displayErrorMessage( null, e.toString() );
			}
			
			g =myg;
	    }
		
	}
	
	private JMenuBar getTheMenuBar() {
		JMenuBar menuBar =new JMenuBar();
		
		menuBar.add( getJMenuFile() );
		menuBar.add( getJMenuEdit() );
		menuBar.add( getJMenuHelp() );		
		
		return menuBar;
	}
	
	private JToolBar getJToolBar() {
		JToolBar jToolBar =new JToolBar();
		
		JButton newButton =getToolBarButton( "ic_new2", "New thematic map" );
		newButton.addActionListener( new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				newMLThematicMap();	
			}
			
		});
		
		jToolBar.add( newButton );
		
		JButton openButton =getToolBarButton( "ic_open", "Open a thematic map..." );
		openButton.addActionListener( new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				openMultiLayerThematicMap();
			}
			
		});
		
		jToolBar.add( openButton );
		
		JButton saveButton =getToolBarButton( "ic_save", "Save thematic map" );
		saveButton.addActionListener( new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				saveMultiLayerThematicMap();
			}			
			
		});
		jToolBar.add( saveButton );
		
		JButton printButton =getToolBarButton( "ic_print", "Print thematic map..." );
		printButton.addActionListener( new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				printThematicMap();
			}
			
		});	
		jToolBar.add( printButton );

		JButton fullScreenButton =getToolBarButton( "ic_fullscreen", "Full screen" );
		fullScreenButton.addActionListener( new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				displayFullScreen();
			}
			
		});
		jToolBar.add( fullScreenButton );
		
		jToolBar.addSeparator();
		
		JButton clipboardCopyButton =getToolBarButton( "ic_copy", "Clipboard copy" );
		clipboardCopyButton.addActionListener( new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				clipboardCopy();
			}
			
		});		
		jToolBar.add( clipboardCopyButton );
		
		jToolBar.addSeparator();
		
		JButton helpButton =getToolBarButton( "ic_help", "Help" );
		helpButton.addActionListener( new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				miscUIHelper.showHelp();
			}
			
		});
		jToolBar.add( helpButton );
				
		return jToolBar;
	}
	
	private JButton getToolBarButton( String iconFilePrefix, String toolTipText ) {
		JButton jButton =new JButton();
		
		jButton.setIcon( miscHelper.getImageIcon( "toolbar/" + iconFilePrefix + "_off.gif" ) );
		jButton.setPressedIcon( miscHelper.getImageIcon( "toolbar/" + iconFilePrefix + "_on.gif" ) );
		jButton.setRolloverIcon( miscHelper.getImageIcon( "toolbar/" + iconFilePrefix + "_roll.gif" ) );
		jButton.setSelectedIcon( miscHelper.getImageIcon( "toolbar/" + iconFilePrefix + "_on.gif" ) );

		jButton.setBorderPainted(false);
		jButton.setContentAreaFilled(false);
		jButton.setFocusPainted(false);
		
		jButton.setToolTipText( toolTipText );
		
		return jButton;
	}
	
	private JMenu getJMenuFile() {
		JMenu menuFile =new JMenu("File");
		menuFile.setMnemonic('F');
		
		menuFile.add( getFileNew() );
		menuFile.add( getFileOpen() );
		menuFile.add( getFileSave() );
		menuFile.add( getFileSaveAs() );
		menuFile.addSeparator();
		menuFile.add( getFileFullScreen() );
		menuFile.add( getFilePrint() );
		menuFile.addSeparator();
		menuFile.add( getFileRecentFiles() );
		menuFile.add( separatorFileRecentFiles );
		menuFile.add( getFileExit() );
		
		return menuFile;
	}
	
	private JMenuItem getFileNew() {
		JMenuItem menuItemFileNew =new JMenuItem( "New thematic map" );
		menuItemFileNew.setMnemonic('N');
		
		menuItemFileNew.addActionListener( new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				newMLThematicMap();
			}
			
		});
		
		return menuItemFileNew;
	} 	
	
	private JMenuItem getFileOpen() {
		JMenuItem menuItemFileOpen =new JMenuItem( "Open thematic map..." );
		menuItemFileOpen.setMnemonic('O');
		
		menuItemFileOpen.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				openMultiLayerThematicMap();
			}
		});	
		
		return menuItemFileOpen;
	} 
	
	private JMenuItem getFileSave() {
		JMenuItem menuItemFileSave =new JMenuItem( "Save thematic map" );
		menuItemFileSave.setMnemonic('S');
		
		menuItemFileSave.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				saveMultiLayerThematicMap();
			}
		});			
		
		return menuItemFileSave;
	} 	
	
	private JMenuItem getFileSaveAs() {
		JMenuItem menuItemFileSaveAs =new JMenuItem( "Save thematic map as..." );
		menuItemFileSaveAs.setMnemonic('a');
		
		menuItemFileSaveAs.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				saveMultiLayerThematicMapAs();
			}
		});	
		
		return menuItemFileSaveAs;
	} 	
	
	private JMenuItem getFileFullScreen() {
		JMenuItem menuItemFileFullScreen =new JMenuItem( "Full screen..." );
		menuItemFileFullScreen.setMnemonic('F');
		
		menuItemFileFullScreen.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				displayFullScreen();
			}
		});	
		
		return menuItemFileFullScreen;		
	}
	
	private JMenuItem getFilePrint() {
		JMenuItem menuItemFilePrint =new JMenuItem( "Print..." );
		menuItemFilePrint.setMnemonic('P');
		
		menuItemFilePrint.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				printThematicMap();
			}
		});		
		
		return menuItemFilePrint;		
	}
	
	private void addRecentFile( String path ) {
		recFileHelper.addFile( path );
		
		if ( recFileHelper.getFiles().size() > 0 ) {
			menuItemFileRecentFiles.removeAll();
	    	  
			addRecentFilesSubMenu( menuItemFileRecentFiles );
		}
		
		menuItemFileRecentFiles.setVisible( recFileHelper.getFiles().size() > 0 );
		separatorFileRecentFiles.setVisible( menuItemFileRecentFiles.isVisible() );
	}
	
	
	private void addRecentFilesSubMenu( JMenu menuRecentFile ) {
		
		List<String> recFilePaths = recFileHelper.getFiles();
		for( String filePath : recFilePaths ) {
			JMenuItem menuItemFileRecentFile =new JMenuItem( filePath );
			
			menuItemFileRecentFile.addActionListener( new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					
				  	JMenuItem menuRecentFile = (JMenuItem)e.getSource();
				  	
				  	multiLayerUIHelper.openMultiLayerThematicMapFromArgument( menuRecentFile.getText(), thisFrame );
				  	
				}
			} );
			
			menuRecentFile.add( menuItemFileRecentFile );
		}
		
	}
	
	private JMenuItem getFileRecentFiles() {
		menuItemFileRecentFiles =new JMenu( "Recent Files" );
		menuItemFileRecentFiles.setMnemonic('R');

		addRecentFilesSubMenu( menuItemFileRecentFiles );
		
		menuItemFileRecentFiles.setVisible( recFileHelper.getFiles().size() > 0 );
		separatorFileRecentFiles.setVisible( menuItemFileRecentFiles.isVisible() );
	
		return menuItemFileRecentFiles;		
	}		
	
	private JMenuItem getFileExit() {
		JMenuItem menuItemFileExit =new JMenuItem( "Exit" );
		menuItemFileExit.setMnemonic('E');
		
		menuItemFileExit.addActionListener( new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				quitApplication();
			}
		});
		
		return menuItemFileExit;		
	}		
	
	private void closeWindow() {
		if ( saveRequired() != JOptionPane.CANCEL_OPTION ) {
			newMLThematicMap();
			setVisible( false );	
		}
	}

	private JMenu getJMenuEdit() {
		JMenu menuEdit =new JMenu( "Edit" );
		menuEdit.setMnemonic('e');
		
		menuEdit.add( getEditCopyToClipboard() );
		
		return menuEdit;
	}
	
	private JMenuItem getEditCopyToClipboard() {
		JMenuItem menuItemEditCopyToClipboard =new JMenuItem( "Copy to clipboard" );
		menuItemEditCopyToClipboard.setMnemonic('C');
		
		menuItemEditCopyToClipboard.addActionListener( new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				clipboardCopy();
			}
			
		});
		
		return menuItemEditCopyToClipboard;		
	}
	
	private JMenu getJMenuHelp() {
		JMenu jMenuHelp =new JMenu( "Help" );
		jMenuHelp.setMnemonic( 'H' );
		jMenuHelp.setText("Help");
			
		jMenuHelp.add( getJMenuItemHelpPreferences() );

		jMenuHelp.add( getJMenuItemHelpHelp() );		
		jMenuHelp.add( getJMenuItemHelpLicense() );
		jMenuHelp.addSeparator();				
		jMenuHelp.add( getJMenuItemHelpAbout() );

		return jMenuHelp;
	}
	
	private JMenuItem getJMenuItemHelpPreferences() {
		
		JMenuItem jMenuItemHelpPreferences =new JMenuItem( "Preferences" );
		jMenuItemHelpPreferences.setVisible(false);
		jMenuItemHelpPreferences.addActionListener(new ActionListener() { 
			public void actionPerformed(ActionEvent e) {
				JDialogPreferences jDialogPreferences =new JDialogPreferences( thisFrame, "Preferences", true );
				jDialogPreferences.setVisible( true );
			}
		});
		
		return jMenuItemHelpPreferences;
		
	}
	
	private JMenuItem getJMenuItemHelpHelp() {
		JMenuItem jMenuItemHelpHelp =new JMenuItem( "Help" );
		jMenuItemHelpHelp.setMnemonic( 'H' );
		jMenuItemHelpHelp.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				miscUIHelper.showHelp();
			}
		});
		
		return jMenuItemHelpHelp;
	}

	private JMenuItem getJMenuItemHelpLicense() {
		JMenuItem jMenuItemHelpLicense = new JMenuItem( "License" );
		jMenuItemHelpLicense.setMnemonic( 'L' );
		jMenuItemHelpLicense.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JDialogLicense jDialogLicense =new JDialogLicense( 
															JOptionPane.getFrameForComponent(thisFrame), 
															true );
					
				jDialogLicense.setVisible( true );
			}
		});

		return jMenuItemHelpLicense;
	}	
	
	private JMenuItem getJMenuItemHelpAbout() {
		JMenuItem jMenuItemHelpAbout =new JMenuItem( "About TMap Designer" );
		jMenuItemHelpAbout.setMnemonic( 'A' );
		
		jMenuItemHelpAbout.addActionListener(new ActionListener() { 
				public void actionPerformed(ActionEvent e) {  
					MappingToolKitFactory mtkf =MappingToolKitFactory.getInstance();
					String libVersion ="Unknown";
					try {
						libVersion = mtkf.getGraphicToolKit().getVersion();
					} catch (TMapException e1) {
					}
					
					JDialogAbout aboutBox =new JDialogAbout( JOptionPane.getFrameForComponent(thisFrame), true, 
															 libVersion, new MiscUIHelper().getBuildDate() );
					
					aboutBox.setVisible( true );					
				}
			});
		
		return jMenuItemHelpAbout;
	}
	
	@EventSubscriber(eventClass=MultiLayerCheckBoxListClicked.class)
    public void onEvent(MultiLayerCheckBoxListClicked evt) {
		getRightSide().repaint();
		
		dirty =true;

    }
	
	private void newMLThematicMap() {
		if ( saveRequired() != JOptionPane.CANCEL_OPTION ) { 
			getJList().removeAll();
			
			getRightSide().setBackground( Color.WHITE );
			getRightSide().repaint();
			
			dirty =false;
		}
	}
	
	private void openMultiLayerThematicMap() {
		MultiLayerThematicMap openedMltm =(MultiLayerThematicMap)multiLayerUIHelper.openMultiLayerThematicMap( this );
		
		if ( openedMltm != null ) {
			
			if ( saveRequired() != JOptionPane.CANCEL_OPTION ) {	
				setMultiLayerThematicMap( openedMltm );
			}
			
			if ( openedMltm.getFile() != null ) {
				addRecentFile( openedMltm.getFile().getPath() );
			}
		}
	}
	
	public void setMultiLayerThematicMap( MultiLayerThematicMap openedMltm ) {
			
		getRightSide().setBackground( Color.WHITE );
		getJList().removeAll();
		
		List<ThematicMapLayer> savLayers =new ArrayList<ThematicMapLayer>();
		
		for( int ct =0; ct < openedMltm.getLayers().size(); ct++ ) {					
			ThematicMapLayer layer =openedMltm.getLayers().get( ct );
			
			try {
				ThematicMapLayer savLayer =(ThematicMapLayer)BizObjectFactory.getInstance().get( BizObjects.THEMATIC_MAP_LAYER );
				
				savLayers.add( savLayer );
				
				savLayer.setBaseMapVisible( layer.isBaseMapVisible() );
				savLayer.setCdVisible( layer.isCdVisible() );
				savLayer.setAnnotationsVisible( layer.isAnnotationsVisible() );
				savLayer.setVisible( layer.isVisible() );
				
				layer.setVisible( true );
				layer.setBaseMapVisible( true );
				layer.setCdVisible( true );
				layer.setAnnotationsVisible( true );
				
			} catch (TMapException e) {
				logger.error( e );
			}
			
			getJList().add( layer );
			getJList().check( ct, layer.isVisible() );
		}
		
	    // Force display, repaint() is not enough in this case
	    getRightSide().paintImmediately( 0, 0, getRightSide().getWidth(), getRightSide().getHeight() );
		
		for( int ct =0; ct < openedMltm.getLayers().size(); ct++ ) {
			ThematicMapLayer layer =openedMltm.getLayers().get( ct );
			
			layer.setVisible( savLayers.get( ct ).isVisible()  );
			layer.setBaseMapVisible( savLayers.get( ct ).isBaseMapVisible()  );
			layer.setCdVisible( savLayers.get( ct ).isCdVisible()  );
			layer.setAnnotationsVisible( savLayers.get( ct ).isAnnotationsVisible()  );					
		}
		
		// Display the thematic map with the correct visibility
		getRightSide().repaint();		
		
		mltm.setFile( openedMltm.getFile() );
		mltm.setLayers( openedMltm.getLayers() );
		
		displayTitle();
		
		dirty =false;

	}
	
	private void saveMLTM( boolean saveAsNew ) {
		if ( getJList().getNbElements() > 0 || dirty ) {
			File file =null;
			
			if ( saveAsNew ) {
				file =multiLayerUIHelper.saveAs( this, mltm, getRightSide().getWidth(), getRightSide().getHeight() );				
			} else {
				file =multiLayerUIHelper.save( this, mltm, getRightSide().getWidth(), getRightSide().getHeight() );				
			}
			
			if ( file != null ) {
				mltm.setFile( file );
				
				displayTitle();
				
				//recFileHelper.addFile( file.getPath() );
				addRecentFile( file.getPath() );
				
				dirty =false;
			}
		} else {
			miscUIHelper.displayWarningMessage( this, "There is nothing to save: the thematic map is blank !" ); 
			
		}
	}
	
	private void saveMultiLayerThematicMap() {
		saveMLTM( false );
	}
	
	private void saveMultiLayerThematicMapAs() {
		saveMLTM( true );
	}
	
	private int saveRequired() {
		int answer =JOptionPane.NO_OPTION;
		
		if ( dirty && getJList().getNbElements() > 0 ) {
			answer = JOptionPane.showConfirmDialog( this, "Do you want to save the current multi-layer thematic map ?");
			
		    if ( answer == JOptionPane.YES_OPTION ) {
		    	saveMultiLayerThematicMap();
		    }
		    
		    if ( answer != JOptionPane.CANCEL_OPTION ) {
		    	dirty =false;
		    }
		}
		
		return answer;
	}
	
	public void printThematicMap() {
		if ( getJList().getNbElements() > 0 ) {
			PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
	
			aset.add( new Copies(1) );
			aset.add( new JobName( "TMap multi-layer thematic map", null ) );
	
			/* Create a print job */
			PrinterJob pj = PrinterJob.getPrinterJob();
			if ( pj.getPrintService() != null ) {

				updateMLTM();
				
				pj.setPrintable( mltk );
				
			    try {
					if( pj.printDialog(aset) ) {
						pj.print(aset);
					}
				} catch (PrinterException pe) { 
					logger.error(pe);
				}
				
				getRightSide().repaint();
				
			} else {
			    miscUIHelper.displayErrorMessage( this, "No available printer!" );
			}		
		} else {
			miscUIHelper.displayWarningMessage( this, "There is nothing to print: the thematic map is blank !" ); 
		}
	}
	
	public void displayFullScreen() {
		if ( getJList().getNbElements() > 0 ) {
			final JFrameThematicMapFullScreen dlg =new JFrameThematicMapFullScreen();
			
			dlg.addWindowListener( new WindowAdapter() {
				
				public void windowOpened(WindowEvent e) {

					updateMLTM();

					try {
						BufferedImage bi = mltk.retrieveThematicMapImage( dlg.getScreenDimension().width
							    								  		, dlg.getScreenDimension().height );
						dlg.setImage( bi );
					} catch (Exception e1) {
						miscUIHelper.displayErrorMessage( null, e1.toString() );
					}

				}
				
			} );

			dlg.setVisible( true );
		} else {
			miscUIHelper.displayWarningMessage( this, "There is nothing to display : the thematic map is blank !" ); 
		}
	}
	
	private void updateMLTM() {
		mltm.getLayers().clear();
		
		Object[] tms =getJList().elements();
		
		for( int ct =0; ct < tms.length; ct++ ) {
			mltm.getLayers().add( (ThematicMapLayer)tms[ ct ] );
			((ThematicMapLayer)tms[ ct ]).setVisible( getJList().isChecked( ct ) );
		}
	}
	
	private List<ThematicMapLayer> buildMapList() {
		List<ThematicMapLayer> list =new ArrayList<ThematicMapLayer>();
		
		Object[] tms =getJList().elements();
		
		for( int ct =0; ct < tms.length; ct++ ) {
				
			if ( getJList().isChecked( ct) ) {
				list.add( (ThematicMapLayer)tms[ ct ] );
			}
		}
		
		return list;
	}
	
	private void clipboardCopy() {
		if ( getJList().getNbElements() > 0 ) {
			try {
				updateMLTM();
				
				mltk.clipboardCopy( getRightSide().getWidth(), getRightSide().getHeight() );
			} catch (Exception e) {
				miscUIHelper.displayErrorMessage( this, e.getMessage() );
			}
			
		} else {
			
			miscUIHelper.displayWarningMessage( this, "There is nothing to copy: the thematic map is blank !" ); 
		}
	}
	
}
